package biolabs.model.vis.j3d;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.media.j3d.Appearance;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TransparencyAttributes;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import biolabs.space.Continuous3DPosition;
import biolabs.space.Discrete3DPosition;
import biolabs.space.GridShape;
import biolabs.space.GridShapedObject;
import biolabs.visual.Visualizer;

public class GridShaped3DObjectVisualizer
implements Visualizer<GridShapedObject<Discrete3DPosition, Continuous3DPosition>, Node> {

	private final Appearance appearance;
	private final float transparency = 0.5f;
	private final Color3f color = new Color3f(1f, 0.4f, 0.4f);

	public GridShaped3DObjectVisualizer() {
		Material m = new Material();
		m.setAmbientColor(color);
		appearance = new Appearance();
		appearance.setMaterial(m);
		TransparencyAttributes tr = new TransparencyAttributes(TransparencyAttributes.FASTEST, transparency);
		appearance.setTransparencyAttributes(tr);
	}

	@Override
	public Node visualize(GridShapedObject<Discrete3DPosition, Continuous3DPosition> o) {

		GridShape<Discrete3DPosition, Continuous3DPosition> shape = o.getShape();
		Set<Discrete3DPosition> border = shape.getBorderElements();
		Set<Discrete3DPosition> shapeElems = shape.getElements();
		List<SquareFacet> facets = new LinkedList<SquareFacet>();
		for(Discrete3DPosition elem: border) {
			for(int axis=0; axis<3; ++axis) {
				int c = elem.getCoordinate(axis);
				Discrete3DPosition posNeighbor = elem.withChangedCoordinate(axis, c+1);
				if(!shapeElems.contains(posNeighbor))
					facets.add(new SquareFacet(elem, axis, true));
				Discrete3DPosition negNeighbor = elem.withChangedCoordinate(axis, c-1);
				if(!shapeElems.contains(negNeighbor))
					facets.add(new SquareFacet(elem, axis, false));
			}
		}

		QuadArray geom = new QuadArray(4*facets.size(), QuadArray.COORDINATES | QuadArray.NORMALS);
		Iterator<SquareFacet> it = facets.iterator();
		for(int i=0; it.hasNext(); ++i) {
			SquareFacet facet = it.next();
			geom.setCoordinates(4*i, facet.vertices);
			for(int j=4*i; j<4*i+4; ++j)
				geom.setNormal(j, facet.normal);
		}

		return new Shape3D(geom, appearance);
	}

	private class SquareFacet {
		final Point3d[] vertices;
		final Vector3f normal;
		public SquareFacet(Discrete3DPosition elem, int axis, boolean direction) {
			int axis2 = (axis+1)%3;
			int axis3 = (axis+2)%3;
			Discrete3DPosition vertex0 = direction ? elem.withChangedCoordinate(axis, elem.getCoordinate(axis)+1) : elem;
			Discrete3DPosition vertex1 = vertex0.withChangedCoordinate(axis2, vertex0.getCoordinate(axis2)+1);
			Discrete3DPosition vertex2 = vertex1.withChangedCoordinate(axis3, vertex1.getCoordinate(axis3)+1);
			Discrete3DPosition vertex3 = vertex0.withChangedCoordinate(axis3, vertex0.getCoordinate(axis3)+1);

			vertices = new Point3d[] {
				new Point3d(vertex0.getX(), vertex0.getY(), vertex0.getZ()),
				new Point3d(vertex1.getX(), vertex1.getY(), vertex1.getZ()),
				new Point3d(vertex2.getX(), vertex2.getY(), vertex2.getZ()),
				new Point3d(vertex3.getX(), vertex3.getY(), vertex3.getZ()),
			};

			Discrete3DPosition normal = Discrete3DPosition.ZERO.withChangedCoordinate(axis, direction ? 1 : -1);
			this.normal = new Vector3f(normal.getX(), normal.getY(), normal.getZ());
		}
	}

}
